Logged in as: guest Log in
Lab 4 mcmillan / Version 5

Editing and Compiling C in Linux

February 22, 2013


Prelab (complete before lab)

    This lab will be cover more aspects of C programming. As such, you will be asked to program on a Unix machine. Before the lab, make sure you can access your CS account and log on to a CS server (i.e. classroom.cs.unc.edu).

    Prior to the lab, login to a CS server and type the following commands:

    $ which pico
    $ which gcc
    

    Verify that each of these commands returns a path, and not an error message of the form,

    filename: Command not found.

Part 1:

    Programming in the Unix environment usually means programming with a text editor.  Traditionally, this means writing your programs without the bells and whistles that Integrated Development Environments (IDEs) come with. However, even the Unix editors increasingly have function browsing and autocompletion.  The most common editors used by programmers in Unix-like systems (including Linux) are Vi (or Vim) and Emacs.  However, these programs have steep learning curves due to their reliance an many unfamiliar hot-key combinations.  There are more familiar editors, though, such as Pico.

    Start a new C program by typing

    $ pico lab4.c

    After typing this, your terminal will have switched from the command prompt to the text editor.  The commands for this editor should appear at the bottom of the page.  Try typing something and saving using '^O', meaning 'control + O'.


    Let's start a C program from scratch.  Delete anything you have written in lab4.c. (You can use 'control-k' to delete a whole line.)

    Every C program has a main() function.  Let's write the main function and have it return the integer 0.

    Let's write the classic first C program "hello.c". C provides many libraries with functions you can use.  These libraries are added using the '#include' preprocesor directive.  Let's include 'stdio.h', which has input & output functions.  Include the line '#include ' at the top of your program. The '#include <(header file)>' directive takes the contents of the named header file and appends them to your program.  This can give you access to functions that are declared and defined within the header file.  For printf, stdio.h in fact only contains the declaration of the function.  The function definition itself is part of the C runtime library. Next we'll write code  to print values out to the console. The primary function for outputing is "printf()"; The first argument to "printf()" is a string, and in the simplest case, printf() merely outputs the given string to the console. Thus, one of the simplest possible C programs is merely:

    #include <stdio.h>
    
    int main() {
        printf("Hello World!\n");
        return 0;
    } 

    Now we're ready to compile.  Save the contents of your editor's buffer. From the command prompt, use 'gcc' to compile your program as follows:

    $ gcc lab4.c

    This call should terminate with no warnings, errors, or other output.  So where is the program?  By default, gcc creates an executable called 'a.out'.  Run 'a.out' by typing

    $ ./a.out

    You should see 'Hello World!' printed back to the command prompt.

    To give your program a name other than 'a.out', you can use the '-o' flag:

    $ gcc lab4.c -o lab4

    Now you can run the program with 

    $./lab4

    Checkoff 1: Remove the "\n" chracter from the printf() string, and recompile. What is the differenece in the ouput with and without these two extra chracters?

Part 2:

    Let us look more into the printf() function.  The functions you have seen previously have had a fixed number of arguments.  However, printf() can have a variable number of arguments, and the number of arguments is determined by the 'string' given to the function as its first argument.  This type of function is called 'variadic', and the declaration features an ellipsis to indicate that the number of arguments may vary from call-to-call:

    int myVariadicFunction(int a, int b, ...)

    Accessing the variable arguments for this function requires special macros in C, which we will not discuss further here.

    As the name suggests, the printf() 'string' specifies both the number of arguments as well as the format for printing each one, thus it is often called a 'format string'. The string specifies the type of argument, and how it will be printed. For example,

    printf("%f %d %x %s", 4.3, 16, 16, "string")

    specifies that a floating-point number, a decimal integer, a hexadecimal integer, and a string will be printed out, each separated by a space.  

    One interesting thing about format strings is that they can be stored and loaded instead of being constructed manually for each use. To do so, we can pass in a C string as the first argument to printf(). Briefly, a C string is simply an array of characters, such as

    char c[] = "a simple string";

    or

    char b[42];

    which respectively create the strings "a simple string" and an empty 42 character long string.

    You can also define an array of strings as follows:

    char *sArray[] = {
        "First String\n",
        "Second String\n",
        "Third String\n",
        "Last String\n"
    };
    

    Checkoff 2: Write a program that stores 5 format strings, each with 0-4 spaces preceding a single character and a new line, e.g. the first three format strings will be "%c\n", " %c\n", and "  %c\n". Use these format strings to print out 13 lines in a zigzag pattern.

    The companion input function to printf() is called scanf().  scanf is another variadic function and uses the same format string as printf, though in this usage, the format string specifies the number, types, and expected formatting of the inputs:

    int a,b,c,d;
    scanf("%d, %d, %d, %d", &a, &b, &c, &d);  // Read a line of 4 comma separated values. 
    char str[256];
    scanf("%s", str); // Read a string (up to a space) from the user.
    

    One caveat is that scanf expects to get the addresses of the variables that it inputs, hence the & sign preceding a through d.

    C has other input and output functions that you should be aware of, including getchar() and putchar(), which respectively load and print one character from/to standard in/out, and gets() and puts(), which do the same for strings instead of single characters.

Part 3:

    The C language has a number of standard math operators built in. The behavior of these operators is type dependent. To illustrate this, enter the following program as 'lab4b.c':

    
    #include  <stdio.h>
    
    int main() {
        int x = 15;
        int y = 4;
        float z = 15.0;
        char c = 'a';
    
        printf("x = %d, y = %d, z = %f, c = %c\n", x, y, z, c);
        printf("Division: x/y = %d, z/y = %f, y*(x/y)=%d, x%y = %d\n", x/y, z/y, y*(x/y), x%y); 
        printf("chars as ints: c = %c, c = %d\n", c, c);
        printf("chars in expressions: c/y = %d\n", c/y);
        printf("shifting: x = %d, x = %x, x<<4 = %d, x<<4 = %x\n", x, x, x<<4, x<<4);
        printf("anding: x&1 = %d, y&1 = %d, x&8 = %d, y&8 = %d\n", x&1, y&1, x&8, y&8);
        printf("floats as ints: z = %f, z = %d, z = %x\n", z, z, z);
        printf("negation: x = %d, -x = %d, -x = %x\n", x, -x, -x);
        printf("complement: x = %d, ~x = %d, ~x = %x\n", x, ~x, ~x);
        printf("expressions: (c-(x*y))%16 = %d, (c-(x*y))&0xf = %d\n", (c-(x*y))%16, (c-(x*y))&0xf);
        return 0;
    }
    
    

    Before you compile and run the program, predict what outputs you expect.

    Checkoff 3: Compile and run the given program. Write down the values output. Were there any surprises?

    Now we turn our attention to math operations that are not built in to C, but instead are provided as functions in a standard library. C has mathematical functions declared in the 'math.h' header file. Let us create a new program, 'lab4c.c', and include this header and stdio.h. Again, have the program return '0', which is a common convention to indicate a normal termination.

    As it is, we can compile the program and run it without any problems, though it does not do much. Now let us add a call to the square root function, 'sqrt()'. Create a float variable called a with the value of 144. Use printf and sqrt to print the value of the square root af a.

    When we try to compile this program, we should get an error such as

    /tmp/ccnV0ryY.o: In function `main':
    lab4c.c:(.text+0x29): undefined reference to `sqrt'
    collect2: ld returned 1 exit status
    

    This means that, though sqrt() was declared, the function definition could not be found. We need to tell the compiler to link the math library, which contains the assembled version of the sqrt() code we are looking for. To do this, we use the -l flag and the name of the library. For the math library, the flag is simply '-lm'. Now compile the program with

    $ gcc lab4c.c -lm -o lab4c
    $ lab4c
    

    The program should now print out the square root.

    Now let's use the absolute value function. Add the lines

    float b = -5.2;
    b = abs(b);
    printf("%f", b);
    

    to the main() function of your program. 

    Checkoff 4: Does the output match what you would expect? Find documentation for the C math library. How might this be changed to get the output you would expect?

Part 4:

    C allows you to specify integers in hexadecimal using values using the prefix '0x'. For example, adding

    int hex_val = 0x00000010;
    printf("%d", hex_val);
    

    to the body of main() will print the number "16".

     

    How does one know how many bits the integer has? C has an operator called 'sizeof' that returns the number of bytes used to represent a type.

    printf("%d", sizeof(int));

    will print 4.  Therefore an integer in C is 4 bytes or 32 bits (the same as our word length in MIPS). The file 'stdint.h' provides constants for the maximum and minimum values of each data type.  For example, INT32_MAX and INT32_MIN are the maximum representable 32-bit int value and the minimum representable 32-bit int value respectively.  

    Checkoff 5: Write C code to load an integer variable with the maximum using the hexadecimal initialization. (Convert the binary representation of the maximum value to hexadecimal.  The value will be in 2s compliment format.) Add one to this value and report on the result.



Posted by jhi on 2013-02-22 15:23:43 (references Version 5)
Add comments here
Posted by duozhao on 2013-02-22 19:52:33 (references Version 5)

<a href="http://www.csbio.unc.edu/mcmillan/index.py?run=Wiki&page=$Comp411S13.Lab%204#top">Click to the Top</a>

Posted by duozhao on 2013-02-22 19:54:04 (references Version 5)
Posted by duozhao on 2013-02-22 19:54:04 (references Version 5)



Site built using pyWeb version 1.10
© 2010 Leonard McMillan, Alex Jackson and UNC Computational Genetics